
/* GCSx
** WPREVIEW.CPP
**
** Widget that previews an image (tile, map, etc.)
*/

/*****************************************************************************
** Copyright (C) 2003-2006 Janson
**
** This program is free software; you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation; either version 2 of the License, or
** (at your option) any later version.
** 
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program; if not, write to the Free Software
** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
*****************************************************************************/

#include "all.h"

WPreview::WPreview(int pId, SDL_Surface* const* src, int myWidth, int myHeight, int pShowMultiple, int pAllowDistort) : Widget(pId, blankString, NULL) { start_func
    assert(src);

    showMultiple = pShowMultiple;
    allowDistort = pAllowDistort;
    
    source = src;
    scaled = NULL;
    scaledIsSource = 1;

    changeSize(myWidth, myHeight);
}

WPreview::~WPreview() { start_func
    if (scaled) SDL_FreeSurface(scaled);
}

void WPreview::changeSize(int newWidth, int newHeight) { start_func
    displayW = newWidth;
    displayH = newHeight;

    if ((newWidth <= 0) || (newHeight <= 0)) {
        resize(0, 0);
    }
    else {
        resize(newWidth + PREVIEW_BORDER_SIZE * 2, newHeight + PREVIEW_BORDER_SIZE * 2);
    }
}

int WPreview::event(int hasFocus, const SDL_Event* event) { start_func
    // Currently doesn't do any events
    return 0;
}

int WPreview::refuseAll() const { start_func
    // Currently doesn't do any events
    return 1;
}

void WPreview::load() { start_func
    if ((*source == NULL) || (displayW <= 0) || (displayH <= 0)) {
        if (scaled) SDL_FreeSurface(scaled);
        scaled = NULL;
        scaledIsSource = 1;
    }
    else {
        SDL_Surface* src= *source;
        int actualW = src->w;
        int actualH = src->h;
        
        // Scaling required?
        if (actualW <= displayW) {
            imageFullX = 1;
            imageScaleX = 1.0;
        }
        else {
            imageFullX = 0;
            imageScaleX = (float)(displayW) / (float)(actualW);
        }
    
        if (actualH <= displayH) {
            imageFullY = 1;
            imageScaleY = 1.0;
        }
        else {
            imageFullY = 0;
            imageScaleY = (float)(displayH) / (float)(actualH);
        }
        
        // Prevent distortion?
        if ((!allowDistort) && ((!imageFullX) || (!imageFullY))) {
            imageFullX = imageFullY = 0;
            
            if (imageScaleX > imageScaleY) imageScaleX = imageScaleY;
            else imageScaleY = imageScaleX;
        }
        
        // Calculate actual w/h (round up; limit to display size max)
        actualW = min(displayW, (int)ceil(actualW * imageScaleX));
        actualH = min(displayH, (int)ceil(actualH * imageScaleY));
        
        // Multiple copies? (only if not scaled)
        int copies = 1;
        int gutters = 0;
        if ((showMultiple) && (imageFullX) && (imageFullY)) {
            // Non-tiled copies must have at least one pixel between them
            // Room for at least two copies?
            if (displayW >= actualW * 2 + 1) {
                copies = 2;
                gutters = 1;
            }
            
            // Room for two copies and 2x? tiled?
            if (displayW >= actualW * 4 + 2) {
                // Room for two copies and 3x? tiled?
                if (displayW >= actualW * 5 + 2) copies = 5;
                else copies = 4;
                gutters = 2;
            }
        }
        
        // Size of gutters and l/r margins; gutters get twice as much space
        int gutterSize = (displayW - actualW * copies) / (gutters + 1);
        int marginSize = (displayW - actualW * copies) / (gutters * 2 + 2);
        // Margins can be 0, gutters must be at least 1
        if (gutterSize < 1) gutterSize = 1;
        
        // Position of primary image
        // Offset x/y by border amount
        imageX = marginSize + PREVIEW_BORDER_SIZE;
        imageY = (displayH - actualH) / 2 + PREVIEW_BORDER_SIZE;
        
        // Secondary image
        if (copies > 1) {
            imageWhiteX = marginSize + actualW + gutterSize + PREVIEW_BORDER_SIZE;
            imageWhiteY = (displayH - actualH) / 2 + PREVIEW_BORDER_SIZE;
        }
        else {
            imageWhiteX = imageWhiteY = 0;
        }
        
        // Tiled image
        if (copies > 2) {
            imageTiledQty = copies - 2;
            imageTiledX = marginSize + actualW * 2 + gutterSize * 2 + PREVIEW_BORDER_SIZE;
            // (can be negative, but if so, ensure rounds in right direction)
            if (actualH * imageTiledQty > displayH) imageTiledY = (displayH - actualH * imageTiledQty - 1) / 2 + PREVIEW_BORDER_SIZE;
            else imageTiledY = (displayH - actualH * imageTiledQty) / 2 + PREVIEW_BORDER_SIZE;
        }
        else {
            imageTiledX = imageTiledY = imageTiledQty = 0;
        }
        
        // Need to redraw actual image? Always redraw if scaled at all
        if ((!imageFullX) || (!imageFullY)) {
            // Deallocate
            if (scaled) SDL_FreeSurface(scaled);
            scaled = NULL;
            scaledIsSource = 1;
            
            // If scaling fails to allocate surface, we won't throw an error-
            // we'll just stick to the original source version
            scaled = sge_transform_surface(src, SDL_MapRGBA(src->format, 0, 0, 0, 0), 0, imageScaleX, imageScaleY, SGE_TAA|SGE_TSAFE);
            if (scaled) {
                scaledIsSource = 0;
            }
            else {
                // (ptr already NULL)
                scaledIsSource = 1;
            }
        }
        // Stick with copy of source if not really scaled
        else {
            // Deallocate?
            if (scaled) SDL_FreeSurface(scaled);
            scaled = NULL;
            scaledIsSource = 1;
        }
    }
    
    setDirty();
}

void WPreview::apply() { start_func
    // Doesn't apply anything
}

void WPreview::display(SDL_Surface* destSurface, Rect& toDisplay, const Rect& clipArea, int xOffset, int yOffset) { start_func
    assert(destSurface);

    if (visible) {
        // If dirty, redraw all
        if (dirty) {
            getRect(toDisplay);
            toDisplay.x += xOffset;
            toDisplay.y += yOffset;
            dirty = 0;
            intersectRects(toDisplay, clipArea);
        }
        
        // Anything to draw?
        if (toDisplay.w) {
            SDL_SetClipRect(destSurface, &toDisplay);

            xOffset += x;
            yOffset += y;
        
            // We specifically use black, except for "white" copy
            SDL_FillRect(destSurface, &toDisplay, SDL_MapRGB(destSurface->format, 0, 0, 0));
            // @TODO: Corners of inverted box aren't grey
            if (PREVIEW_BORDER_SIZE) drawGuiBoxInvert(xOffset, yOffset, width, height, PREVIEW_BORDER_SIZE, destSurface);
            
            // Just one copy?            
            if (!imageWhiteX) {
                // (scaled images have a 1-pixel border we want to discard)
                if (scaled) blit(1, 1, scaled, xOffset + imageX, yOffset + imageY, destSurface, scaled->w - 2, scaled->h - 2);
                else if ((scaledIsSource) && (*source)) blit(0, 0, *source, xOffset + imageX, yOffset + imageY, destSurface);
            }
            else {
                int tileW = (*source)->w;
                int tileH = (*source)->h;

                SDL_SetAlpha(*source, SDL_SRCALPHA, 255);

                // Multiple copies- never scaled
                // Black copy
                blit(0, 0, *source, xOffset + imageX, yOffset + imageY, destSurface);
                
                // White copy, with alpha
                drawRect(xOffset + imageWhiteX, yOffset + imageWhiteY, tileW, tileH, SDL_MapRGB(destSurface->format, 255, 255, 255), destSurface);
                blit(0, 0, *source, xOffset + imageWhiteX, yOffset + imageWhiteY, destSurface);
                
                // Tiles
                if (imageTiledX) {
                    // Reduce clip rectangle to within border vertically, tiled copy may go out
                    Rect newClip = toDisplay;
                    if (newClip.y == yOffset) {
                        ++newClip.y;
                        --newClip.h;
                    }
                    if (newClip.y + newClip.h == yOffset + height) --newClip.h;
                    SDL_SetClipRect(destSurface, &newClip);

                    for (int tX = 0; tX < imageTiledQty; ++tX)
                        for (int tY = 0; tY < imageTiledQty; ++tY)
                            blit(0, 0, *source, xOffset + imageTiledX + tileW * tX, yOffset + imageTiledY + tileH * tY, destSurface);
                }

                SDL_SetAlpha(*source, 0, 255);
            }
        }
    }
}

